﻿namespace MapleRock.P2PEmulator.RGR
{
  using System;
  using System.Collections.Generic;
  using System.Diagnostics;
  using System.Linq;
  using System.Text;

  using MapleRock.P2PEmulator.Core;

  internal sealed class RgrExperimentSample
  {
    private Network network = new Network();

    private ModelSampleParams param = null;

    public RgrExperimentSample(int runsNumber, ModelSampleParams param)
    {
      Debug.Assert(runsNumber > 1, "runsNumber > 1");

      this.RunsNumber = runsNumber;
      this.Runs = new RgrExperimentRun[runsNumber];

      Debug.Assert(param.PeersNumber > 1, "param.PeersNumber > 1");
      Debug.Assert(param.Dimensions > 1, "param.Dimensions > 1");
      Debug.Assert(param.Dimensions < 20, "param.Dimensions < 20");
      Debug.Assert(param.MaxPeerLinks > 2, "param.MaxPeerLinks > 2");
      Debug.Assert(param.MaxPeerLinks < 21, "param.MaxPeerLinks < 21");
      Debug.Assert(param.MessageLossProb >= 0, "param.MessageLossProb >= 0");
      Debug.Assert(param.MessageLossProb <= 0.1, "param.MessageLossProb <= 0.1");

      this.param = param;
    }

    public int RunsNumber { get; private set; }

    public RgrExperimentRun[] Runs { get; private set; }

    public void Go()
    {
      ModelOutputFile.Instance.BeginSample(this.param.PeersNumber);

      ExperimentLog.Instance.LogEvent("{0} runs to be executed...", this.RunsNumber);

      this.InitializeModel();

      for (var runIndex = 0; runIndex < this.RunsNumber; ++runIndex)
      {
        ExperimentLog.Instance.LogEvent("Performing run {0}/{1}", runIndex + 1, this.RunsNumber);

        this.network.ResetState();

        Debug.Assert(this.Runs[runIndex] == null, "this.Runs[runIndex] == null");
        this.Runs[runIndex] = new RgrExperimentRun();
        this.Runs[runIndex].Go(this.network);
      }

      this.EvaluateStats();

      ModelOutputFile.Instance.EndSample();

      // free a memory occupied by the sample network model
      this.network = null;
      GC.Collect();
    }

    private void EvaluateStats()
    {
      Debug.Assert(this.param.VariableParameter != 0, "this.param.VariableParameter != 0");

      for (var i = 0; i < this.Runs.Length; ++i)
      {
        // average
        var average = this.Runs[i].AvgCount;
        average.Parameter = this.param.VariableParameter;

        // minimum
        var min = this.Runs[i].MinCount;
        min.Parameter = this.param.VariableParameter;

        // maximum
        var max = this.Runs[i].MaxCount;
        max.Parameter = this.param.VariableParameter;
      }
    }

    private void InitializeModel()
    {
      Debug.Assert(this.param.PeersNumber > 1, "this.param.PeersNumber > 1");
      Debug.Assert(this.param.Dimensions > 1, "this.param.Dimensions > 1");
      Debug.Assert(this.param.MessageLossProb >= 0, "this.param.MessageLossProb >= 0");
      Debug.Assert(this.param.MessageLossProb <= 0.01, "this.param.MessageLossProb <= 0.01");
      Debug.Assert(this.param.MaxPeerLinks > 2, "this.param.MaxPeerLinks > 2");

      var inputData = new XmlInputData();
      inputData.Dimensions = this.param.Dimensions;
      inputData.PeersNumber = this.param.PeersNumber;
      inputData.UpdDeliveryFailureProbability = this.param.MessageLossProb;
      inputData.MaxPeerLinksAllowed = this.param.MaxPeerLinks;

      inputData.InfoSpaceBounds = string.Empty;
      for (var i = 0; i < inputData.Dimensions; ++i)
      {
        inputData.InfoSpaceBounds += "0 10 ";
      }

      inputData.InfoSpaceBounds = inputData.InfoSpaceBounds.Trim();

      inputData.Geography = new XmlGeographyData[] { new XmlGeographyData() };
      inputData.Geography[0].AreaLatitude = 300;
      inputData.Geography[0].AreaLongitude = 300;
      inputData.Geography[0].CitiesNumber = 10;
      inputData.Geography[0].CitySizeMax = 10;
      inputData.Geography[0].CitySizeMin = 1;

      this.network.Initialize(inputData);
    }
  }
}
